#ifndef VECTOR3D_H
#define VECTOR3D_H

//Standard library headers
#include <cmath>
#include <iostream>

#define PI 3.14159265358979323846

//TODO: Consider templating this class to N-dimensions

//Operator definitions
//  Cross pruduct = 	^
//  Dot product =   	%
//  2D cross product = 	*

class Vector3D
{
public:
	
	/*friend std::istream& operator >> (std::istream &isInput, Vector3D& obj);*/
	friend std::ostream& operator << (std::ostream &osResult, const Vector3D& obj);
	
	//Scaler type definition
	typedef double scalar;

private:
    
    //Array to hold vector data
	scalar V[3];
	
public:

	//Initalisation constructor
	Vector3D(scalar X = 0, scalar Y = 0, scalar Z = 0) { V[0]=X; V[1]=Y; V[2]=Z; }
	
	
	//Array access operator
	scalar operator [] (const unsigned i) const	{ return V[i]; }
	scalar &operator [] (const unsigned i) { return V[i]; }
	
	//Copy constructor
	Vector3D(const Vector3D &vec) { V[0]=vec[0]; V[1]=vec[1]; V[2]=vec[2]; }
	
	//Function to return vector array
	scalar* getArray()					{ return V; }
	
	//Element access functions
	//TODO: Could replace these with a union?
	//      Perhaps could have many different names as well, e.g., (i, j, k), (x, y, z) etc.
	scalar x()	const				{ return V[0]; }
	scalar y()	const				{ return V[1]; }
	scalar z()	const				{ return V[2]; }
	scalar &x()						{ return V[0]; }
	scalar &y()						{ return V[1]; }
	scalar &z()						{ return V[2]; }
	
	//friend std::ostream& operator <<(std::ostream &os,const Vector3D &obj);
	
	//Vector assignment
	const Vector3D &operator = (const Vector3D &vec)
	{
		V[0] = vec[0];
		V[1] = vec[1];
		V[2] = vec[2];
		
		return *this;
	}
	
	//Scalar assignment
	//TODO: Decide if this should be allowed
	const Vector3D &operator = (const scalar &s)
	{
		V[0] = s;
		V[1] = s;
		V[2] = s;
		
		return *this;
	}
	
	//Vector equality
	const bool operator == (const Vector3D &vec) const
	{
	    return ((V[0] == vec[0]) && (V[1] == vec[1]) && (V[2] == vec[2]));
	}
	
	//Vector inequality
	const bool operator != (const Vector3D &vec) const
	{
	    return !(*this == vec);
	}
	
	//Vector add
	const Vector3D operator + (const Vector3D &vec) const
	{
 	    return Vector3D(V[0] + vec[0], V[1] + vec[1], V[2] + vec[2]);
	}
	
	//Vector add (opposite of negation)
	const Vector3D operator + () const
	{    
	    return Vector3D(*this);
	}
	
	//Vector increment
	const Vector3D& operator += (const Vector3D& vec)
	{    
 	    V[0] += vec[0];
	  	V[1] += vec[1];
	  	V[2] += vec[2];
	  	
	  	return *this;
	}
	
	//Vector subtraction
	const Vector3D operator - (const Vector3D& vec) const
	{    
	    return Vector3D(V[0] - vec[0], V[1] - vec[1], V[2] - vec[2]);
	}
	
	//Vector negation
	const Vector3D operator - () const
	{    
	   return Vector3D(-V[0], -V[1], -V[2]);
	}
	
	//Vector decrement
	const Vector3D &operator -= (const Vector3D& vec)
	{
	    V[0] -= vec[0];
	    V[1] -= vec[1];
	    V[2] -= vec[2];
	
	    return *this;
	}
	
	//scalar self-multiply
	const Vector3D &operator *= (const scalar &s)
	{
	    V[0] *= s;
	    V[1] *= s;
	    V[2] *= s;
	  
	    return *this;
	}
	
	//Scalar self-divide
	const Vector3D &operator /= (const scalar &s)
	{
	    const scalar recip = 1/s; // for speed, one divecision
	
	    V[0] *= recip;
	    V[1] *= recip;
	    V[2] *= recip;
	
	    return *this;
	}
	
	//Post multiply by scalar
	const Vector3D operator * (const scalar &s) const
	{
	    return Vector3D(V[0]*s, V[1]*s, V[2]*s);
	}
	
	//Pre multiply by scalar
	friend inline const Vector3D operator*(const scalar &s, const Vector3D &vec)
	{
	    return vec*s;
	}
	
	//Divide by scalar
	const Vector3D operator / (scalar s) const
	{
	    s = 1/s;
	
	    return Vector3D(s*V[0], s*V[1], s*V[2]);
	}
	
	//Cross product
	const Vector3D getCrossProduct(const Vector3D &vec) const
	{
	    return Vector3D(V[1]*vec[2] - V[2]*vec[1], V[2]*vec[0] - V[0]*vec[2], V[0]*vec[1] - V[1]*vec[0]);
	}
	
	//Cross product
	const Vector3D operator ^ (const Vector3D &vec) const
	{
	    return Vector3D(V[1]*vec[2] - V[2]*vec[1], V[2]*vec[0] - V[0]*vec[2], V[0]*vec[1] - V[1]*vec[0]);
	}
	
	//Dot product
	const scalar DotProduct(const Vector3D &vec) const
	{
	    return V[0]*vec[0] + V[1]*vec[1] + V[2]*vec[2];
	}
	
	//Dot product
	const scalar operator % (const Vector3D &vec) const
	{
	    return V[0]*vec[0] + V[1]*vec[1] + V[2]*vec[2];
	}
	
	//2D cross product
	const scalar operator * (const Vector3D &vec) const
	{
	    return (V[0]*vec[1] - V[1]*vec[0]);
	}
	
	//Length of vector
	const scalar getLength() const
	{
	  return (scalar)sqrt((double)(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]));
	}
	//Deprecated
	const scalar Length() const
	{
	  return (scalar)sqrt((double)(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]));
	}
	
	//Length squared of vector
	const scalar getLength2() const
	{
	  return (scalar)(double)(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
	}
	//Deprecated
	const scalar Length2() const
	{
	  return (scalar)(double)(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
	}
	
	//Return the unit vector
	const Vector3D getUnitVector() const
	{
	  return (*this) / Length();
	}
	//Deprecated
	const Vector3D UnitVector() const
	{
	  return (*this) / Length();
	}
	
	//Return the 2D normal vector
	const Vector3D getNormal() const
	{
	  return Vector3D(V[1], -V[0], 0);
	}
	//Deprecated
	const Vector3D Normal() const
	{
	  return Vector3D(V[1], -V[0], 0);
	}
	
	//Normalise this vector
	void normalize()
	{
	  (*this) /= Length();
	}
	//Deprecated
	void Normalize()
	{
	  (*this) /= Length();
	}
	
	//Return angle of vector in degrees
	const scalar getAngle() const
	{
		
		if (V[0] == 0){
			if (V[1] > 0)
				return -90;
			else
				return 90;
		}
		
		scalar a = (scalar)atan((double)(V[1] / V[0])) * 180 / PI;
		
	    if (V[0] > 0.0f)
			return (a + 180);
		else
			return a;
	}
	
	//Zero the vector 
	void zero()
	{
		//TODO: Make sure I did this right.
		V[0] = V[1] = V[2] = 0;
	}
	
	/*//Return vector with specified length
	const Vector3D operator | (const scalar length) const
	{
	  return *this * (length / !(*this));
	}*/
	
	/*// set length of Vector3D equal to length
	const Vector3D& operator |= (const float length)
	{
	  return *this = *this | length;
	}*/
	
	// return angle between two vectors
	/*const float inline Angle(const Vector3D& normal) const
	{
	     return acosf(*this % normal);
	}*/
	
	// reflect this vector off surface with normal vector
	/*const Vector3D inline Reflection(const Vector3D& normal) const
	{
	     const Vector3D vec(*this | 1);     // normaliV[2]e this Vector3D
	     return (vec - normal * 2.0 * (vec % normal)) * !*this;
	}*/

};

//Stream operators
//----------------
/*inline std::istream& operator >> (std::istream &isInput, Vector3D& obj)
{
	//TODO: Consider implementing this.
	return isInput;
}*/
//NOTE: The inline here is actually necessary to prevent linker errors
inline std::ostream& operator << (std::ostream &osResult, const Vector3D& obj)
{
	osResult << "("  << obj.V[0]
	         << ", " << obj.V[1]
	         << ", " << obj.V[2]
	         << ")";
	return osResult;
}
//----------------

#endif //VECTOR3D_H
